package com.abel.bigwater.kmgw

import com.abel.bigwater.data.BcdString
import com.abel.bigwater.dataserver.model.PacketException
import com.abel.bigwater.dataserver.model.PacketNotEnoughException
import com.abel.bigwater.dataserver.model.h86a.H86AHistoryData
import com.abel.bigwater.dataserver.model.h86a.H86ARealtimeData
import com.abel.bigwater.dataserver.model.hgd.HgdUdpData
import com.abel.bigwater.dataserver.model.hgd.HgdaoTcpHeart
import io.netty.buffer.ByteBuf
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.ByteToMessageDecoder
import org.slf4j.LoggerFactory

class IwgDecoder : ByteToMessageDecoder() {
    /**
     * Decode the from one [ByteBuf] to an other. This method will be called till either the input
     * [ByteBuf] has nothing to read when return from this method or till nothing was read from the input
     * [ByteBuf].
     *
     * @param ctx           the [ChannelHandlerContext] which this [ByteToMessageDecoder] belongs to
     * @param in            the [ByteBuf] from which to read data
     * @param out           the [List] to which decoded messages should be added
     * @throws Exception    is thrown if an error occurs
     */
    override fun decode(ctx: ChannelHandlerContext?, `in`: ByteBuf?, out: MutableList<Any>?) {
        if (`in` == null) {
            lgr.error("null byte-buffer")
            return
        }
        val msg = BcdString.hexString(`in`, withBlank = true)
        lgr.info("got bytes from ${ctx?.channel()?.remoteAddress()}:\r\n$msg")

        // udp-data
        try {
            var parsed = false
            while (`in`.readableBytes() > 0) {
                try {
                    val p = HgdUdpData()
                    if (p.parseBuffer(`in`, true)) {
                        out?.add(p)
                        parsed = true
                    } else {
                        break
                    }
                } catch (ex: PacketNotEnoughException) {
                    lgr.error("not enough bytes for hgd data: ${ex.message}")
                    break
                }
            }
            if (parsed) return

            // tcp-heart
            while (`in`.readableBytes() > 0) {
                try {
                    val p = HgdaoTcpHeart()
                    if (p.parseBuffer(`in`)) {
                        out?.add(p)
                        parsed = true
                    } else {
                        break
                    }
                } catch (ex: PacketNotEnoughException) {
                    lgr.error("not enough bytes for hgd heart: ${ex.message}")
                    break
                }
            }
            if (parsed) return

            // history-data
            while (`in`.readableBytes() > 0) {
                try {
                    val p = H86AHistoryData()
                    if (p.parseBuffer(`in`, true)) {
                        out?.add(p)
                        parsed = true
                    } else {
                        break
                    }
                } catch (ex: PacketNotEnoughException) {
                    lgr.error("not enough bytes for h86a history: ${ex.message}")
                    break
                }
            }
            if (parsed) return

            // realtime-data
            while (`in`.readableBytes() > 0) {
                try {
                    val p = H86ARealtimeData()
                    if (p.parseBuffer(`in`, true)) {
                        out?.add(p)
                        parsed = true
                    } else {
                        break
                    }
                } catch (ex: PacketNotEnoughException) {
                    lgr.error("not enough bytes for h86a realtime: ${ex.message}")
                    break
                }
            }
            if (parsed) return
        } catch (ex: PacketException) {
            lgr.error("IWG数据解析错误: ${ex.message}, ${ex.message}", ex)

            ctx?.close()
        }
    }

    override fun exceptionCaught(ctx: ChannelHandlerContext?, cause: Throwable?) {
        lgr.error("IWG数据解析失败: ${ctx?.channel()?.remoteAddress()}, ${cause?.message}", cause)

        ctx?.close()
    }

    companion object {
        private val lgr = LoggerFactory.getLogger(IwgDecoder::class.java)
    }
}